#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef WIN32
#include "windows.h"
#else
#include <sys/time.h>
#include <time.h>
#endif
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "stdarg.h"
#include "math.h"
#include "assert.h"

#include "List.h"

#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"
#include "Ufeatureterm.h"
#include "AUfeatureterm.h"
#include "FT_refinement.h"
#include "FT_generalization.h"

#include "SpecialTerm.h"
#include "ST_floatInterval.h"

#include "psm.h"

#include "AUdistance.h"


#ifndef WIN32
struct timeval init_tick_count_value;

void setupTickCount()
{
	gettimeofday(&init_tick_count_value, NULL);
}

long GetTickCount()
{
	struct timeval now;
	gettimeofday(&now, NULL);
	struct timeval diff;
	diff.tv_sec = now.tv_sec - init_tick_count_value.tv_sec;
	diff.tv_usec = now.tv_usec - init_tick_count_value.tv_usec;
	if (diff.tv_usec < 0)
	{
		diff.tv_sec--;
		diff.tv_usec+=1000000;
	}
	return diff.tv_sec*1000 + diff.tv_usec/1000;
}
#endif



Prediction *KNN_AUdistance(List<FeatureTerm> *descriptions,List<FeatureTerm> *solutions,FeatureTerm *problem,int K,Ontology *o,FTKBase *dm);



int main(void)
{
	int t_start,t_end;

#ifndef WIN32
	setupTickCount();
#endif

	Ontology *o=new Ontology();
	FTKBase *case_base=new FTKBase(o);
	FTKBase *domain_model=new FTKBase(o);

	domain_model->create_boolean_objects(o);
	case_base->uses(domain_model);

	printf("Stats 0: %i %i\n",Symbol::stats_nSymbols(),Symbol::stats_nSymbolReferences());

	ST_floatInterval::createSort(o);

	{
/*
		domain_model->ImportNOOS("../NOOS/artificial-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/artificial-dm.noos",o);
		case_base->ImportNOOS("../NOOS/artificial-512.noos",o);
*/

/*
		domain_model->ImportNOOS("../NOOS/zoology-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/zoology-DM.noos",o);
		case_base->ImportNOOS("../NOOS/zoology-cases-101.noos",o);
*/

/*
		domain_model->ImportNOOS("../NOOS/soybean-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/soybean-dm.noos",o);
		case_base->ImportNOOS("../NOOS/soybean-cases-307.noos",o);
*/


		domain_model->ImportNOOS("../NOOS/sponge-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/sponge-dm.noos",o);
//		case_base->ImportNOOS("../NOOS/sponge-cases-503.noos",o);
		case_base->ImportNOOS("../NOOS/sponge-cases-280.noos",o);
//		case_base->ImportNOOS("../NOOS/sponge-cases-120.noos",o);
//		case_base->ImportNOOS("../NOOS/sponge-cases-test.noos",o);


/*
//      OLD FILES: --------------------------------------------------------------------
		domain_model->ImportNOOS("../NOOS/toxicology-eva/toxicology-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/toxicology-eva/toxicology-dm.noos",o);
		case_base->ImportNOOS("../NOOS/toxicology-eva/toxicology-descriptions.noos",o);
//		case_base->ImportNOOS("../NOOS/toxicology-eva/toxicology-descriptions-50.noos",o);
		case_base->ImportNOOS("../NOOS/toxicology-eva/toxicology-problems.noos",o);
		case_base->ImportNOOS("../NOOS/toxicology-eva/toxicology-cases-502.noos",o);
//		case_base->ImportNOOS("../NOOS/toxicology-eva/toxicology-cases-50.noos",o);
*/

/*
//      NEW FILES: --------------------------------------------------------------------
		domain_model->ImportNOOS("../NOOS/toxic-eva-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/toxic-eva-dm.noos",o);
//		case_base->ImportNOOS("../NOOS/toxic-eva-cases-10.noos",o);
//		case_base->ImportNOOS("../NOOS/toxic-eva-cases-50.noos",o);
		case_base->ImportNOOS("../NOOS/toxic-eva-cases-100.noos",o);
//		case_base->ImportNOOS("../NOOS/toxic-eva-cases-371.noos",o);
*/

/*
		domain_model->ImportNOOS("../NOOS/family-ontology.noos",o);
		domain_model->ImportNOOS("../NOOS/family-dm.noos",o);
		case_base->ImportNOOS("../NOOS/family-cases-12.noos",o);
//		case_base->ImportNOOS("../NOOS/family-cases-12-sets.noos",o);
*/
		printf("Stats 1: %i %i\n",Symbol::stats_nSymbols(),Symbol::stats_nSymbolReferences());

		printf("There are %i + %i undefined terms.\n",domain_model->get_n_undefined_terms(),case_base->get_n_undefined_terms());

		// Show ontology:
/*
		{
			int n=o->get_nsorts();
			List<Sort> *l;
			int i;
			Sort *s;
			
			for(i=0;i<n;i++) {
				s=o->get_sort_num(i);
				printf("Sort: %s\n",s->get());
				printf("Subsorts:\n");
				l=s->getSubSorts();
				l->Rewind();
				while(l->Iterate(s)) {
					printf("    %s\n",s->get());
				} // while
				delete l;
			} // for
		}
*/

		t_start=GetTickCount();

		{
			Path *dp=new Path,*sp=new Path;
			Prediction *solution;

			List<FeatureTerm> *cases;
			List<FeatureTerm> case_descriptions,case_solutions;
			FeatureTerm *problem,*problem_description,*problem_solution;

			dp->Add(new Symbol("description"));
			sp->Add(new Symbol("solution"));
			sp->Add(new Symbol("order"));
//			sp->Add(new Symbol("m-rats"));

//			cases=case_base->SearchFT(o->get_sort("artificial-data-problem"));
//			cases=case_base->SearchFT(o->get_sort("zoo-problem"));
			cases=case_base->SearchFT(o->get_sort("sponge-problem"));
//			cases=case_base->SearchFT(o->get_sort("soybean-problem"));
//			cases=case_base->SearchFT(o->get_sort("uncle-problem"));
//			cases=case_base->SearchFT(o->get_sort("toxic-problem"));

			cases->Randomize();

			{
				FeatureTerm *c,*cd,*cs;
				cases->Rewind();
				while(cases->Iterate(c)) {
//					printf("%s\n",c->getName()->get());
					cd=c->featureValueQuick("description");
//					cs=c->featureValue("solution");
					cs=c->featureValueQuick("solution");
					cs=cs->featureValueQuick("order");
//					cs=cs->featureValue("m-rats");
					case_descriptions.Add(new FeatureTerm(cd));
					case_solutions.Add(new FeatureTerm(cs));
				} // while
			}

			/* Leave one out: */ 
			{
				int i;
				int size=cases->Length();
				int correct=0,incorrect=0;
				int pos;
				List<FeatureTerm> solutions;
				List<int> solutions_correct;
				List<int> solutions_incorrect;
				FeatureTerm *predicted_solution;

//				for(i=0;i<1;i++) {
				for(i=0;i<size;i++) {
					printf("%i/%i\n",i,size);
					problem=cases->ExtractIni();
					problem_description=case_descriptions.ExtractIni();
					problem_solution=case_solutions.ExtractIni();

					solution=0;
					
					// K-NN using AU-distance:
//					solution=KNN_AUdistance(&case_descriptions,&case_solutions,problem_description,7,o,domain_model);
//					solution=KNN_AUdistance(&case_descriptions,&case_solutions,problem_description,5,o,domain_model);
//					solution=KNN_AUdistance(&case_descriptions,&case_solutions,problem_description,3,o,domain_model);
					solution=KNN_AUdistance(&case_descriptions,&case_solutions,problem_description,1,o,domain_model);

					if (solution!=0) {
						int i;
						int support;
						predicted_solution=0;
						
						for(i=0;i<solution->solutions.Length();i++) {
							if (predicted_solution==0 || *(solution->support[i])>support) {
								predicted_solution=solution->solutions[i];
								support=*(solution->support[i]);
							} // if
						} // for
					} else {
						predicted_solution=0;
					} // if

					if (predicted_solution!=0 && *predicted_solution==*problem_solution) {
						printf("OK\n\n");

						correct++;
						pos=solutions.Position(problem_solution);
						if (pos==-1) {
							solutions.Add(new FeatureTerm(problem_solution));
							solutions_correct.Add(new int(1));
							solutions_incorrect.Add(new int(0));
						} else {
							(*(solutions_correct[pos]))++;
						} /* if */ 
					} else {
						printf("Fail\n\n");

						incorrect++;
						pos=solutions.Position(problem_solution);
						if (pos==-1) {
							solutions.Add(new FeatureTerm(problem_solution));
							solutions_correct.Add(new int(0));
							solutions_incorrect.Add(new int(1));
						} else {
							(*(solutions_incorrect[pos]))++;
						} /* if */ 
					} /* if */ 

					delete solution;

					cases->Add(problem);
					case_descriptions.Add(problem_description);
					case_solutions.Add(problem_solution);

					fflush(0);
				} /* for */ 

				printf("\nFinal results:\n");
				printf("Accuracy: %g%%  (%i %i)\n",float(100*correct)/float(correct+incorrect),correct,incorrect);
				printf("\nIndividual class results:\n");
				{
					int i;
					char *s;

					for(i=0;i<solutions.Length();i++) {
						s=solutions[i]->toStringNOOS(domain_model);
						printf("%s  %i %i\n",s,*(solutions_correct[i]),*(solutions_incorrect[i]));
						delete []s;
					} /* for */ 
				}
				printf("\n");
	
			}
			
			AUdistance_leaf_patterns_clear_cache();

			cases->ExtractAll();
			delete cases;

			delete dp;
			delete sp;

		}

		t_end=GetTickCount();

		printf("Time taken: %i\n",t_end-t_start);

		printf("Stats 2: %i %i\n",Symbol::stats_nSymbols(),Symbol::stats_nSymbolReferences());
	}

	delete case_base;
	delete domain_model;

	printf("Stats 3: %i %i\n",Symbol::stats_nSymbols(),Symbol::stats_nSymbolReferences());

	delete o;

	printf("Stats 4: %i %i\n",Symbol::stats_nSymbols(),Symbol::stats_nSymbolReferences());
	Symbol::stats_printSymbolStats();

	Symbol::reset();

	return 0;
} /* main */ 




Prediction *KNN_AUdistance(List<FeatureTerm> *descriptions,List<FeatureTerm> *solutions,FeatureTerm *problem,int K,Ontology *o,FTKBase *dm)
{
	int i,j,l;
	float *distances;
	int *indexes;
	Prediction *p=0;
		
	l=descriptions->Length();
	
	printf("%i-NN working with %i cases\n",K,l);
	
	distances=new float[l];
	indexes=new int[l];
	
	// Compute all the distances:
	for(i=0;i<l;i++) {
//		distances[i]=AUdistance(problem,descriptions->operator[](i),o,dm);
//		distances[i]=AUdistance_leaf_patterns(problem,descriptions->operator[](i),o,dm);
//		distances[i]=AUdistance_leaf_patterns2(problem,descriptions->operator[](i),o,dm);
		distances[i]=AUdistance_leaf_patterns2w(problem,descriptions->operator[](i),descriptions,solutions,o,dm);
		indexes[i]=i;
//		printf("Distance to %i is %f\n",i,distances[i]);
	} // for
	
	// Sort the list (bubble sort):
	{
		float tmp_f;
		int tmp_i;
		bool change=true;
		
		for(i=0;i<l;i++) {
			change=false;
			for(j=0;j<i;j++) {
				if (distances[j]>distances[j+1]) {
					tmp_f=distances[j];
					distances[j]=distances[j+1];
					distances[j+1]=tmp_f;
					
					tmp_i=indexes[j];
					indexes[j]=indexes[j+1];
					indexes[j+1]=tmp_i;
					
					change=true;
				} // if 
			} // for
		} // for
	}
	
	// Get the top K:
	p=new Prediction(new FeatureTerm(problem));
	for(i=0;i<K;i++) {
		FeatureTerm *solution=solutions->operator[](indexes[i]);
		j=p->solutions.Position(solution);
		printf("Selected %s with distance %f\n",descriptions->operator[](indexes[i])->getName()->get(),distances[i]);
		if (j==-1) {
			p->justifications.Add(new FeatureTerm((Symbol *)0,o->get_sort("any"),0));
			p->solutions.Add(new FeatureTerm(solution));
			p->support.Add(new int(1));
		} else {
			(*(p->support[j]))++;
		} // if
	} // for
		
	delete []distances;
	delete []indexes;
	
	
	/* Show result: */ 
	{
		char *s;
		FeatureTerm *j,*sol;
		int *sup;
		printf("/- %i-NN --------------------------------------------------------\\\n",K);
		printf("* Problem:\n");
		if (p->problem->getName()!=0) {
			s=p->problem->getName()->get();
			printf("  %s\n",s);
		} else {
			printf("?\n");
		} // if
		printf("* %i different solutions\n",p->justifications.Length());
		p->justifications.Rewind();
		p->solutions.Rewind();
		p->support.Rewind();
		while(p->justifications.Iterate(j) &&
			  p->solutions.Iterate(sol) &&
			  p->support.Iterate(sup)) {
			printf("* Solution:\n");
			s=sol->toStringNOOS(dm);
			printf("  %s\n",s);
			delete []s;
			printf("* Justification:\n");
			s=j->toStringNOOS(dm);
			printf("%s\n",s);
			delete []s;
			printf("* Support: %i\n",*sup);
		} // while 
		printf("\\----------------------------------------------------------------/\n");
	}

	
	return p;
} /* KNN_AUdistance */ 




